// World.cpp

#include "stdafx.h"
#include "Socket.h"
#include "World.h"

PlayerState::PlayerState()
{
	m_dwIPID=WorldState::InvalidPlayerID;		
	m_fRotation = 0.0f;		 
	m_fVelocityX = 0.0f;		
	m_fVelocityY = 0.0f;
	m_fAccelerationX = 0.0f;	
	m_fAccelerationY = 0.0f;
	m_fThrust = 0.0f;		
	m_fSteering = 0.0f;		
	m_dwScore = 0;		
	m_fGemPosX = 0.0f;
	m_fGemPosY = 0.0f;
	m_dwGemID = WorldState::InvalidGemID;

	m_fPositionX = 0.0f;
	m_fPositionY = 0.0f;
};

PlayerState::~PlayerState()
{
};

PlayerState::PlayerState(const PlayerState& p_xS)
{
	m_dwIPID=p_xS.m_dwIPID;		
	m_fRotation = p_xS.m_fRotation;		 
	m_fVelocityX = p_xS.m_fVelocityX;		
	m_fVelocityY = p_xS.m_fVelocityY;
	m_fAccelerationX = p_xS.m_fAccelerationX;	
	m_fAccelerationY = p_xS.m_fAccelerationY;
	m_fThrust = p_xS.m_fThrust;		
	m_fSteering = p_xS.m_fSteering;		
	m_dwScore = p_xS.m_dwScore;		
	m_fGemPosX = p_xS.m_fGemPosX;
	m_fGemPosY = p_xS.m_fGemPosY;
	m_dwGemID = p_xS.m_dwGemID;

	m_fPositionX = p_xS.m_fPositionX;
	m_fPositionY = p_xS.m_fPositionY;
};

PlayerState& PlayerState::operator=(const PlayerState& p_xS)
{
	m_dwIPID=p_xS.m_dwIPID;		
	m_fRotation = p_xS.m_fRotation;		 
	m_fVelocityX = p_xS.m_fVelocityX;		
	m_fVelocityY = p_xS.m_fVelocityY;
	m_fAccelerationX = p_xS.m_fAccelerationX;	
	m_fAccelerationY = p_xS.m_fAccelerationY;
	m_fThrust = p_xS.m_fThrust;		
	m_fSteering = p_xS.m_fSteering;		
	m_dwScore = p_xS.m_dwScore;		
	m_fGemPosX = p_xS.m_fGemPosX;
	m_fGemPosY = p_xS.m_fGemPosY;
	m_dwGemID = p_xS.m_dwGemID;

	m_fPositionX = p_xS.m_fPositionX;
	m_fPositionY = p_xS.m_fPositionY;

	return *this;
};

bool PlayerState::IsValid() const
{
	return m_dwIPID!=WorldState::InvalidPlayerID;
};



WorldState::WorldState()
{
	//This is temp population of players in the world
	m_axPlayers.SetSize(MaxPlayers);
	m_axPlayers[0].m_dwIPID = IPAddr::GetLocalDefault().GetHost();
	m_axPlayers[0].m_fPositionX = 10.0f;
	m_axPlayers[0].m_fPositionY = 10.0f;
	m_axPlayers[0].m_fRotation = 0.0f;		 
	m_axPlayers[0].m_fVelocityX = 0.0f;		
	m_axPlayers[0].m_fVelocityY = 0.0f;
	m_axPlayers[0].m_fAccelerationX = 0.0f;	
	m_axPlayers[0].m_fAccelerationY = 0.0f;
	m_axPlayers[0].m_fThrust = 0.0f;		
	m_axPlayers[0].m_fSteering = 0.0f;		
	m_axPlayers[0].m_dwScore = 0;		
	m_axPlayers[0].m_fGemPosX = 0.0f;
	m_axPlayers[0].m_fGemPosY = 0.0f;
	m_axPlayers[0].m_dwGemID = WorldState::InvalidGemID;


	int i;
	for (i = 1; i < MaxPlayers; i++)
	{
		m_axPlayers[i].m_dwIPID = InvalidPlayerID;
		m_axPlayers[i].m_fRotation = 0.0f;		 
		m_axPlayers[i].m_fVelocityX = 0.0f;		
		m_axPlayers[i].m_fVelocityY = 0.0f;
		m_axPlayers[i].m_fAccelerationX = 0.0f;	
		m_axPlayers[i].m_fAccelerationY = 0.0f;
		m_axPlayers[i].m_fThrust = 0.0f;		
		m_axPlayers[i].m_fSteering = 0.0f;		
		m_axPlayers[i].m_dwScore = 0;		
		m_axPlayers[i].m_fGemPosX = 0.0f;
		m_axPlayers[i].m_fGemPosY = 0.0f;
		m_axPlayers[i].m_dwGemID = WorldState::InvalidGemID;

		m_axPlayers[i].m_fPositionX = 0.0f;
		m_axPlayers[i].m_fPositionY = 0.0f;

	};
};

WorldState::~WorldState()
{

}; 

void WorldState::Clear()
{
	int i;
	for(i=0;i<MaxPlayers;i++)
	{
		m_axPlayers[i]=PlayerState();
	};
};

void WorldState::Sort()
{
	PlayerState xTemp;
	int i;
	bool bDone=false;
	while(bDone)
	{
		bDone=true;
		for(i=0;i<MaxPlayers-1;i++)
		{
			if(m_axPlayers[i].m_dwIPID<m_axPlayers[i+1].m_dwIPID)
			{
				xTemp=m_axPlayers[i];
				m_axPlayers[i]=m_axPlayers[i+1];
				m_axPlayers[i+1]=xTemp;
				bDone=false;
			};
		};
	};
};

int WorldState::FindByID(dword p_dwID) const
{
	int i;
	for(i=0;i<MaxPlayers;i++)
	{
		if(m_axPlayers[i].m_dwIPID==p_dwID)
		{
			return i;
		};
	};
	return -1;
};

//void WorldState::Init(float p_fCarRadius, float p_fWorldWidth, float p_fWorldHeight)
//{
//
//};

void WorldState::Update(dword p_dwMyId)
{
	int iMeIdx = FindByID(p_dwMyId);
	if (iMeIdx != -1)
	{
		//Control
		{
			m_axPlayers[iMeIdx].m_fThrust = 0.0f;
			m_axPlayers[iMeIdx].m_fSteering = 0.0f;
			if(::GetAsyncKeyState(VK_UP)&0x8000){m_axPlayers[iMeIdx].m_fThrust=1.0f;};
			if(::GetAsyncKeyState(VK_DOWN)&0x8000){m_axPlayers[iMeIdx].m_fThrust=-1.0f;};
			if(::GetAsyncKeyState(VK_LEFT)&0x8000){m_axPlayers[iMeIdx].m_fSteering=-1.0f;};
			if(::GetAsyncKeyState(VK_RIGHT)&0x8000){m_axPlayers[iMeIdx].m_fSteering=1.0f;};
		}

		//Gem stuff
		{
			if (m_axPlayers[iMeIdx].m_dwGemID == InvalidGemID)
			{
				int currentTime = ::GetTickCount();
				if ((currentTime-m_xGem.m_iLastSpawn)>5000)
				{
					m_xGem.m_dwGemID = ::GetTickCount();
					m_xGem.m_fGemPosX = 20.0f;
					m_xGem.m_fGemPosY = 20.0f;

					m_xGem.m_iLastSpawn = currentTime;

					m_axPlayers[iMeIdx].m_dwGemID = m_xGem.m_dwGemID;
					m_axPlayers[iMeIdx].m_fGemPosX = m_xGem.m_fGemPosX;
					m_axPlayers[iMeIdx].m_fGemPosY = m_xGem.m_fGemPosY;
				};
			};
	};

	}
	//Update Car Stuff
	{
		for(int i = 0; i < m_axPlayers.GetSize(); i++)
		{
			PlayerState* pxCar = &m_axPlayers[i];
			pxCar->m_fAccelerationX = cos(pxCar->m_fRotation)*pxCar->m_fThrust*0.1f;
			pxCar->m_fAccelerationY = sin(pxCar->m_fRotation)*pxCar->m_fThrust*0.1f;
			if(pxCar->m_fVelocityX != 0.0f || pxCar->m_fVelocityY != 0.0f)
			{
				float fAddRot = sqrt(sqrt(pxCar->m_fVelocityX*pxCar->m_fVelocityX+pxCar->m_fVelocityY*pxCar->m_fVelocityY) * 0.01f)*pxCar->m_fSteering;
				if(pxCar->m_fThrust < 0.0f) fAddRot*=-1.0f;
				pxCar->m_fRotation += fAddRot;
			}
		}
	}	
	//UpdatePosVel
	{
		for(int i = 0; i < m_axPlayers.GetSize(); i++)
		{
			PlayerState* pxCar = &m_axPlayers[i];
			pxCar->m_fPositionX += pxCar->m_fVelocityX *=0.95f;
			pxCar->m_fPositionY += pxCar->m_fVelocityY *=0.95f;
			pxCar->m_fVelocityX += pxCar->m_fAccelerationX;
			pxCar->m_fVelocityY += pxCar->m_fAccelerationY;
		}
	}
	//Walls
	{
		for(int i = 0; i < m_axPlayers.GetSize(); i++)
		{
			BounceOffWalls(&m_axPlayers[i]);
		}
	}
	DArray<POINT> axCollisions;
	//Collect Collision
	{
		CollectCollisions(axCollisions);
	}
	//Collision Response
	{
		for(int i = 0; i < axCollisions.GetSize(); i++)
		{
			const POINT& xPair = axCollisions[i];
			CollisionResponse(&m_axPlayers[xPair.x], &m_axPlayers[xPair.y]);
		}
	}
}

void WorldState::BounceOffWalls(PlayerState* p_xP)
{
	float fRadius = (float)Radius;
	float fMaxX = (float)(Width-Radius);
	float fMaxY = (float)(Height-Radius);
	float fElasticity=0.93f;

	p_xP->m_fPositionX=Clamp(p_xP->m_fPositionX,fRadius,fMaxX);
	p_xP->m_fPositionY=Clamp(p_xP->m_fPositionY,fRadius,fMaxY);
	if(p_xP->m_fPositionX+p_xP->m_fVelocityX<fRadius)
	{
		p_xP->m_fVelocityX=abs(p_xP->m_fVelocityX)*fElasticity;
	};
	if(p_xP->m_fPositionX+p_xP->m_fVelocityX>fMaxX)
	{
		p_xP->m_fVelocityX=-abs(p_xP->m_fVelocityX)*fElasticity;
	};
	if(p_xP->m_fPositionY+p_xP->m_fVelocityY<fRadius)
	{
		p_xP->m_fVelocityY=abs(p_xP->m_fVelocityY)*fElasticity;
	};
	if(p_xP->m_fPositionY+p_xP->m_fVelocityY>fMaxY)
	{
		p_xP->m_fVelocityY=-abs(p_xP->m_fVelocityY)*fElasticity;
	};
};

void WorldState::CollectCollisions(DArray<POINT>& po_axPairs)
{
	po_axPairs.Clear();
	float fMinDistSq=(float)(Radius*Radius*4);
	int i,j;
	for(i=0;i<m_axPlayers.GetSize()-1;i++)
	{
		const PlayerState* xA=&m_axPlayers[i];
		//if(xA.IsValid())
		{
			for(j=i+1;j<m_axPlayers.GetSize();j++)
			{
				const PlayerState* xB=&m_axPlayers[j];
				//if(xB.IsValid())
				{
					float fDX=xB->m_fPositionX-xA->m_fPositionX;
					float fDY=xB->m_fPositionY-xA->m_fPositionY;
					if(fMinDistSq>=fDX*fDX+fDY*fDY)
					{
						POINT& xPair=po_axPairs.NewRef();
						xPair.x=i;
						xPair.y=j;
					};
				};
			};
		};
	};
}

void WorldState::CollisionResponse(PlayerState* p_xA,PlayerState* p_xB)
{
	float fW=(float)Width;
	float fH=(float)Height;
	float fR=(float)Radius;

	float fDeltaX,fDeltaY;
	fDeltaX=p_xA->m_fPositionX-p_xB->m_fPositionX;
	fDeltaY=p_xA->m_fPositionY-p_xB->m_fPositionY;
	float fDeltaSq=fDeltaX*fDeltaX+fDeltaY*fDeltaY;
	if(fDeltaSq>fR*fR*4.0f){return;};
	float fDist=sqrt(fDeltaSq);
	float fMTDX=fDeltaX;
	float fMTDY=fDeltaY;
	float fMTDFactor=(fR*2.0f-fDist)/fDist;
	fMTDX*=fMTDFactor*0.5f;
	fMTDY*=fMTDFactor*0.5f;
	p_xA->m_fPositionX+=fMTDX;
	p_xA->m_fPositionY+=fMTDY;
	p_xB->m_fPositionX-=fMTDX;
	p_xB->m_fPositionY-=fMTDY;
	float fNX=fDeltaX;
	float fNY=fDeltaY;
	fNX/=fDist;
	fNY/=fDist;
	float fVX=p_xA->m_fVelocityX-p_xB->m_fVelocityX;
	float fVY=p_xA->m_fVelocityY-p_xB->m_fVelocityY;
	float fVN=fVX*fNX+fVY*fNY;
	if(fVN>0){return;};
	float fI=-0.9f*fVN;
	fNX=fNX*fI;
	fNY=fNY*fI;
	p_xA->m_fVelocityX+=fNX;
	p_xA->m_fVelocityY+=fNY;
	p_xB->m_fVelocityX-=fNX;
	p_xB->m_fVelocityY-=fNY;
	p_xA->m_fPositionX=Clamp(p_xA->m_fPositionX,fR,fW-fR);
	p_xA->m_fPositionY=Clamp(p_xA->m_fPositionY,fR,fH-fR);
	p_xB->m_fPositionX=Clamp(p_xB->m_fPositionX,fR,fW-fR);
	p_xB->m_fPositionY=Clamp(p_xB->m_fPositionY,fR,fH-fR);
}

void WorldState::Render(Bitmap *p_pxBM)
{
	float fRatioH = (float)p_pxBM->m_iH / Height;
	float fRatioV = (float)p_pxBM->m_iW / Height; // maybe it should be width
	float fRatio;
	int iOfsX=0;
	int iOfsY=0;
	if(fRatioH>fRatioV)
	{
		fRatio=fRatioV;
		iOfsX=(int)((p_pxBM->m_iW-(fRatio*Height))*0.5f);
	}
	else
	{
		fRatio=fRatioH;
		iOfsY=(int)((p_pxBM->m_iH-(fRatio*Height))*0.5f);
	};
	if(fRatio>0.01f)
	{
		int iX0, iY0, iX1, iY1;

		int iPlayerRadius=(int)(Radius*fRatio);

		const dword adwColors[]={
			0xffff00,0xff6000,0x00ff00,0xffffff,
			0x2672BE,0x7226BE,0xBE2672,0xBE7226,
			0x72BE26,0xff0000,0x4498EC,0x9844EC,
			0xEC4498,0xEC9844,0x98EC44,0x44EC98
		};

		for(int i=0;i<m_axPlayers.GetSize();i++)
		{
			if (m_axPlayers[i].m_dwIPID != InvalidPlayerID)
			{
				PlayerState* pxCar = &m_axPlayers[i];
				iX0=(int)(pxCar->m_fPositionX*fRatio+iOfsX);
				iY0=(int)(pxCar->m_fPositionY*fRatio+iOfsY);
				dword dwColor=adwColors[i];
				p_pxBM->FillCircle(iX0,iY0,iPlayerRadius,dwColor);
				//draw direction
				float fX0 = cos(pxCar->m_fRotation)*Radius+pxCar->m_fPositionX;
				float fY0 = sin(pxCar->m_fRotation)*Radius+pxCar->m_fPositionY;
				float fX1 = cos(pxCar->m_fRotation)*Radius*-1.0f+pxCar->m_fPositionX;
				float fY1 = sin(pxCar->m_fRotation)*Radius*-1.0f+pxCar->m_fPositionY;
				iX0 = (int)(fX0*fRatio+iOfsX);
				iY0 = (int)(fY0*fRatio+iOfsX);
				iX1 = (int)(fX1*fRatio+iOfsX);
				iY1 = (int)(fY1*fRatio+iOfsX);
				p_pxBM->DrawLine(iX0, iY0, iX1, iY1, dwColor^0xffffff);
			};
		};

		for (int i = 0; i < m_axPlayers.GetSize(); i++)
		{
			if (m_axPlayers[i].m_dwIPID != InvalidPlayerID)
			{
				PlayerState* pxCar = &m_axPlayers[i];
				iX0=(int)(pxCar->m_fGemPosX*fRatio+iOfsX);
				iY0=(int)(pxCar->m_fGemPosY*fRatio+iOfsY);
				dword dwColor=0xffffff;
				p_pxBM->FillCircle(iX0,iY0,GemRadius,dwColor);
			};
		}
	}
}

WorldState::Gem::Gem()
{
	m_fGemPosX = 0.0f;
	m_fGemPosY = 0.0f;

	m_iLastSpawn = ::GetTickCount();

	m_dwGemID = InvalidGemID;
};

WorldState::Gem::~Gem()
{
	m_dwGemID = -1;
};

